1   /*
2    * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions
6    * are met:
7    *
8    *   - Redistributions of source code must retain the above copyright
9    *     notice, this list of conditions and the following disclaimer.
10   *
11   *   - Redistributions in binary form must reproduce the above copyright
12   *     notice, this list of conditions and the following disclaimer in the
13   *     documentation and/or other materials provided with the distribution.
14   *
15   *   - Neither the name of Oracle nor the names of its
16   *     contributors may be used to endorse or promote products derived
17   *     from this software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20   * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  
32  
33  import java.applet.Applet;
34  import java.awt.*;
35  import java.awt.event.*;
36  import java.io.*;
37  import java.net.*;
38  
39  
40  @SuppressWarnings("serial")
41  public class SpreadSheet extends Applet implements MouseListener, KeyListener {
42  
43      String title;
44      Font titleFont;
45      Color cellColor;
46      Color inputColor;
47      int cellWidth = 100;
48      int cellHeight = 15;
49      int titleHeight = 15;
50      int rowLabelWidth = 15;
51      Font inputFont;
52      boolean isStopped = false;
53      boolean fullUpdate = true;
54      int rows;
55      int columns;
56      int currentKey = -1;
57      int selectedRow = -1;
58      int selectedColumn = -1;
59      SpreadSheetInput inputArea;
60      Cell cells[][];
61      Cell current = null;
62  
63      @Override
64      public synchronized void init() {
65          String rs;
66  
67          cellColor = Color.white;
68          inputColor = new Color(100, 100, 225);
69          inputFont = new Font("Monospaced", Font.PLAIN, 10);
70          titleFont = new Font("Monospaced", Font.BOLD, 12);
71          title = getParameter("title");
72          if (title == null) {
73              title = "Spreadsheet";
74          }
75          rs = getParameter("rows");
76          if (rs == null) {
77              rows = 9;
78          } else {
79              rows = Integer.parseInt(rs);
80          }
81          rs = getParameter("columns");
82          if (rs == null) {
83              columns = 5;
84          } else {
85              columns = Integer.parseInt(rs);
86          }
87          cells = new Cell[rows][columns];
88          char l[] = new char[1];
89          for (int i = 0; i < rows; i++) {
90              for (int j = 0; j < columns; j++) {
91  
92                  cells[i][j] = new Cell(this,
93                          Color.lightGray,
94                          Color.black,
95                          cellColor,
96                          cellWidth - 2,
97                          cellHeight - 2);
98                  l[0] = (char) ((int) 'a' + j);
99                  rs = getParameter("" + new String(l) + (i + 1));
100                 if (rs != null) {
101                     cells[i][j].setUnparsedValue(rs);
102                 }
103             }
104         }
105 
106         Dimension d = getSize();
107         inputArea = new SpreadSheetInput(null, this, d.width - 2, cellHeight - 1,
108                 inputColor, Color.white);
109         resize(columns * cellWidth + rowLabelWidth,
110                 (rows + 3) * cellHeight + titleHeight);
111         addMouseListener(this);
112         addKeyListener(this);
113     }
114 
115     public void setCurrentValue(float val) {
116         if (selectedRow == -1 || selectedColumn == -1) {
117             return;
118         }
119         cells[selectedRow][selectedColumn].setValue(val);
120         repaint();
121     }
122 
123     @Override
124     public void stop() {
125         isStopped = true;
126     }
127 
128     @Override
129     public void start() {
130         isStopped = false;
131     }
132 
133     @Override
134     public void destroy() {
135         for (int i = 0; i < rows; i++) {
136             for (int j = 0; j < columns; j++) {
137                 if (cells[i][j].type == Cell.URL) {
138                     cells[i][j].updaterThread.run = false;
139                 }
140             }
141         }
142     }
143 
144     public void setCurrentValue(int type, String val) {
145         if (selectedRow == -1 || selectedColumn == -1) {
146             return;
147         }
148         cells[selectedRow][selectedColumn].setValue(type, val);
149         repaint();
150     }
151 
152     @Override
153     public void update(Graphics g) {
154         if (!fullUpdate) {
155             int cx, cy;
156 
157             g.setFont(titleFont);
158             for (int i = 0; i < rows; i++) {
159                 for (int j = 0; j < columns; j++) {
160                     if (cells[i][j].needRedisplay) {
161                         cx = (j * cellWidth) + 2 + rowLabelWidth;
162                         cy = ((i + 1) * cellHeight) + 2 + titleHeight;
163                         cells[i][j].paint(g, cx, cy);
164                     }
165                 }
166             }
167         } else {
168             paint(g);
169             fullUpdate = false;
170         }
171     }
172 
173     public void recalculate() {
174         int i, j;
175 
176         //System.out.println("SpreadSheet.recalculate");
177         for (i = 0; i < rows; i++) {
178             for (j = 0; j < columns; j++) {
179                 if (cells[i][j] != null && cells[i][j].type == Cell.FORMULA) {
180                     cells[i][j].setRawValue(evaluateFormula(
181                             cells[i][j].parseRoot));
182                     cells[i][j].needRedisplay = true;
183                 }
184             }
185         }
186         repaint();
187     }
188 
189     float evaluateFormula(Node n) {
190         float val = 0.0f;
191 
192         //System.out.println("evaluateFormula:");
193         //n.print(3);
194         if (n == null) {
195             //System.out.println("Null node");
196             return val;
197         }
198         switch (n.type) {
199             case Node.OP:
200                 val = evaluateFormula(n.left);
201                 switch (n.op) {
202                     case '+':
203                         val += evaluateFormula(n.right);
204                         break;
205                     case '*':
206                         val *= evaluateFormula(n.right);
207                         break;
208                     case '-':
209                         val -= evaluateFormula(n.right);
210                         break;
211                     case '/':
212                         val /= evaluateFormula(n.right);
213                         break;
214                 }
215                 break;
216             case Node.VALUE:
217                 //System.out.println("=>" + n.value);
218                 return n.value;
219             case Node.CELL:
220                 if (cells[n.row][n.column] == null) {
221                     //System.out.println("NULL at 193");
222                 } else {
223                     //System.out.println("=>" + cells[n.row][n.column].value);
224                     return cells[n.row][n.column].value;
225                 }
226         }
227 
228         //System.out.println("=>" + val);
229         return val;
230     }
231 
232     @Override
233     public synchronized void paint(Graphics g) {
234         int i, j;
235         int cx, cy;
236         char l[] = new char[1];
237 
238 
239         Dimension d = getSize();
240 
241         g.setFont(titleFont);
242         i = g.getFontMetrics().stringWidth(title);
243         g.drawString((title == null) ? "Spreadsheet" : title,
244                 (d.width - i) / 2, 12);
245         g.setColor(inputColor);
246         g.fillRect(0, cellHeight, d.width, cellHeight);
247         g.setFont(titleFont);
248         for (i = 0; i < rows + 1; i++) {
249             cy = (i + 2) * cellHeight;
250             g.setColor(getBackground());
251             g.draw3DRect(0, cy, d.width, 2, true);
252             if (i < rows) {
253                 g.setColor(Color.red);
254                 g.drawString("" + (i + 1), 2, cy + 12);
255             }
256         }
257 
258         g.setColor(Color.red);
259         cy = (rows + 3) * cellHeight + (cellHeight / 2);
260         for (i = 0; i < columns; i++) {
261             cx = i * cellWidth;
262             g.setColor(getBackground());
263             g.draw3DRect(cx + rowLabelWidth,
264                     2 * cellHeight, 1, d.height, true);
265             if (i < columns) {
266                 g.setColor(Color.red);
267                 l[0] = (char) ((int) 'A' + i);
268                 g.drawString(new String(l),
269                         cx + rowLabelWidth + (cellWidth / 2),
270                         cy);
271             }
272         }
273 
274         for (i = 0; i < rows; i++) {
275             for (j = 0; j < columns; j++) {
276                 cx = (j * cellWidth) + 2 + rowLabelWidth;
277                 cy = ((i + 1) * cellHeight) + 2 + titleHeight;
278                 if (cells[i][j] != null) {
279                     cells[i][j].paint(g, cx, cy);
280                 }
281             }
282         }
283 
284         g.setColor(getBackground());
285         g.draw3DRect(0, titleHeight,
286                 d.width,
287                 d.height - titleHeight,
288                 false);
289         inputArea.paint(g, 1, titleHeight + 1);
290     }
291 
292     //1.1 event handling
293     @Override
294     public void mouseClicked(MouseEvent e) {
295     }
296 
297     @Override
298     public void mousePressed(MouseEvent e) {
299         int x = e.getX();
300         int y = e.getY();
301         Cell cell;
302         if (y < (titleHeight + cellHeight)) {
303             selectedRow = -1;
304             if (y <= titleHeight && current != null) {
305                 current.deselect();
306                 current = null;
307             }
308             e.consume();
309         }
310         if (x < rowLabelWidth) {
311             selectedRow = -1;
312             if (current != null) {
313                 current.deselect();
314                 current = null;
315             }
316             e.consume();
317 
318         }
319         selectedRow = ((y - cellHeight - titleHeight) / cellHeight);
320         selectedColumn = (x - rowLabelWidth) / cellWidth;
321         if (selectedRow > rows
322                 || selectedColumn >= columns) {
323             selectedRow = -1;
324             if (current != null) {
325                 current.deselect();
326                 current = null;
327             }
328         } else {
329             if (selectedRow >= rows) {
330                 selectedRow = -1;
331                 if (current != null) {
332                     current.deselect();
333                     current = null;
334                 }
335                 e.consume();
336             }
337             if (selectedRow != -1) {
338                 cell = cells[selectedRow][selectedColumn];
339                 inputArea.setText(cell.getPrintString());
340                 if (current != null) {
341                     current.deselect();
342                 }
343                 current = cell;
344                 current.select();
345                 requestFocus();
346                 fullUpdate = true;
347                 repaint();
348             }
349             e.consume();
350         }
351     }
352 
353     @Override
354     public void mouseReleased(MouseEvent e) {
355     }
356 
357     @Override
358     public void mouseEntered(MouseEvent e) {
359     }
360 
361     @Override
362     public void mouseExited(MouseEvent e) {
363     }
364 
365     @Override
366     public void keyPressed(KeyEvent e) {
367     }
368 
369     @Override
370     public void keyTyped(KeyEvent e) {
371         fullUpdate = true;
372         inputArea.processKey(e);
373         e.consume();
374     }
375 
376     @Override
377     public void keyReleased(KeyEvent e) {
378     }
379 
380     @Override
381     public String getAppletInfo() {
382         return "Title: SpreadSheet \nAuthor: Sami Shaio \nA simple spread sheet.";
383     }
384 
385     @Override
386     public String[][] getParameterInfo() {
387         String[][] info = {
388             { "title", "string",
389                 "The title of the spread sheet.  Default is 'Spreadsheet'" },
390             { "rows", "int", "The number of rows.  Default is 9." },
391             { "columns", "int", "The number of columns.  Default is 5." }
392         };
393         return info;
394     }
395 }
396 
397 
398 class CellUpdater extends Thread {
399 
400     Cell target;
401     InputStream dataStream = null;
402     StreamTokenizer tokenStream;
403     public volatile boolean run = true;
404 
405     public CellUpdater(Cell c) {
406         super("cell updater");
407         target = c;
408     }
409 
410     @Override
411     public void run() {
412         try {
413             dataStream = new URL(target.app.getDocumentBase(),
414                     target.getValueString()).openStream();
415             tokenStream = new StreamTokenizer(new BufferedReader(
416                     new InputStreamReader(dataStream)));
417             tokenStream.eolIsSignificant(false);
418 
419             while (run) {
420                 switch (tokenStream.nextToken()) {
421                     case StreamTokenizer.TT_EOF:
422                         dataStream.close();
423                         return;
424                     default:
425                         break;
426                     case StreamTokenizer.TT_NUMBER:
427                         target.setTransientValue((float) tokenStream.nval);
428                         if (!target.app.isStopped && !target.paused) {
429                             target.app.repaint();
430                         }
431                         break;
432                 }
433                 try {
434                     Thread.sleep(2000);
435                 } catch (InterruptedException e) {
436                     break;
437                 }
438             }
439         } catch (IOException e) {
440             return;
441         }
442     }
443 }
444 
445 
446 class Cell {
447 
448     public static final int VALUE = 0;
449     public static final int LABEL = 1;
450     public static final int URL = 2;
451     public static final int FORMULA = 3;
452     Node parseRoot;
453     boolean needRedisplay;
454     boolean selected = false;
455     boolean transientValue = false;
456     public int type = Cell.VALUE;
457     String valueString = "";
458     String printString = "v";
459     float value;
460     Color bgColor;
461     Color fgColor;
462     Color highlightColor;
463     int width;
464     int height;
465     SpreadSheet app;
466     CellUpdater updaterThread;
467     boolean paused = false;
468 
469     public Cell(SpreadSheet app,
470             Color bgColor,
471             Color fgColor,
472             Color highlightColor,
473             int width,
474             int height) {
475         this.app = app;
476         this.bgColor = bgColor;
477         this.fgColor = fgColor;
478         this.highlightColor = highlightColor;
479         this.width = width;
480         this.height = height;
481         needRedisplay = true;
482     }
483 
484     public void setRawValue(float f) {
485         valueString = Float.toString(f);
486         value = f;
487     }
488 
489     public void setValue(float f) {
490         setRawValue(f);
491         printString = "v" + valueString;
492         type = Cell.VALUE;
493         paused = false;
494         app.recalculate();
495         needRedisplay = true;
496     }
497 
498     public void setTransientValue(float f) {
499         transientValue = true;
500         value = f;
501         needRedisplay = true;
502         app.recalculate();
503     }
504 
505     public void setUnparsedValue(String s) {
506         switch (s.charAt(0)) {
507             case 'v':
508                 setValue(Cell.VALUE, s.substring(1));
509                 break;
510             case 'f':
511                 setValue(Cell.FORMULA, s.substring(1));
512                 break;
513             case 'l':
514                 setValue(Cell.LABEL, s.substring(1));
515                 break;
516             case 'u':
517                 setValue(Cell.URL, s.substring(1));
518                 break;
519         }
520     }
521 
522     /**
523      * Parse a spreadsheet formula. The syntax is defined as:
524      *
525      * formula -> value
526      * formula -> value op value
527      * value -> '(' formula ')'
528      * value -> cell
529      * value -> <number>
530      * op -> '+' | '*' | '/' | '-'
531      * cell -> <letter><number>
532      */
533     public String parseFormula(String formula, Node node) {
534         String subformula;
535         String restFormula;
536         Node left;
537         Node right;
538         char op;
539 
540         if (formula == null) {
541             return null;
542         }
543         subformula = parseValue(formula, node);
544         //System.out.println("subformula = " + subformula);
545         if (subformula == null || subformula.length() == 0) {
546             //System.out.println("Parse succeeded");
547             return null;
548         }
549         if (subformula.equals(formula)) {
550             //System.out.println("Parse failed");
551             return formula;
552         }
553 
554         // parse an operator and then another value
555         switch (op = subformula.charAt(0)) {
556             case 0:
557                 //System.out.println("Parse succeeded");
558                 return null;
559             case ')':
560                 //System.out.println("Returning subformula=" + subformula);
561                 return subformula;
562             case '+':
563             case '*':
564             case '-':
565             case '/':
566                 restFormula = subformula.substring(1);
567                 subformula = parseValue(restFormula, right = new Node());
568                 //System.out.println("subformula(2) = " + subformula);
569                 if (subformula == null ? restFormula != null : !subformula.
570                         equals(restFormula)) {
571                     //System.out.println("Parse succeeded");
572                     left = new Node(node);
573                     node.left = left;
574                     node.right = right;
575                     node.op = op;
576                     node.type = Node.OP;
577                     //node.print(3);
578                     return subformula;
579                 } else {
580                     //System.out.println("Parse failed");
581                     return formula;
582                 }
583             default:
584                 //System.out.println("Parse failed (bad operator): " + subformula);
585                 return formula;
586         }
587     }
588 
589     public String parseValue(String formula, Node node) {
590         char c = formula.charAt(0);
591         String subformula;
592         String restFormula;
593         float _value;
594         int row;
595         int column;
596 
597         //System.out.println("parseValue: " + formula);
598         restFormula = formula;
599         if (c == '(') {
600             //System.out.println("parseValue(" + formula + ")");
601             restFormula = formula.substring(1);
602             subformula = parseFormula(restFormula, node);
603             //System.out.println("rest=(" + subformula + ")");
604             if (subformula == null
605                     || subformula.length() == restFormula.length()) {
606                 //System.out.println("Failed");
607                 return formula;
608             } else if (!(subformula.charAt(0) == ')')) {
609                 //System.out.println("Failed (missing parentheses)");
610                 return formula;
611             }
612             restFormula = subformula;
613         } else if (c >= '0' && c <= '9') {
614             int i;
615 
616             //System.out.println("formula=" + formula);
617             for (i = 0; i < formula.length(); i++) {
618                 c = formula.charAt(i);
619                 if ((c < '0' || c > '9') && c != '.') {
620                     break;
621                 }
622             }
623             try {
624                 _value = Float.valueOf(formula.substring(0, i)).floatValue();
625             } catch (NumberFormatException e) {
626                 //System.out.println("Failed (number format error)");
627                 return formula;
628             }
629             node.type = Node.VALUE;
630             node.value = _value;
631             //node.print(3);
632             restFormula = formula.substring(i);
633             //System.out.println("value= " + value + " i=" + i +
634             //                     " rest = " + restFormula);
635             return restFormula;
636         } else if (c >= 'A' && c <= 'Z') {
637             int i;
638 
639             column = c - 'A';
640             restFormula = formula.substring(1);
641             for (i = 0; i < restFormula.length(); i++) {
642                 c = restFormula.charAt(i);
643                 if (c < '0' || c > '9') {
644                     break;
645                 }
646             }
647             row = Float.valueOf(restFormula.substring(0, i)).intValue();
648             //System.out.println("row = " + row + " column = " + column);
649             node.row = row - 1;
650             node.column = column;
651             node.type = Node.CELL;
652             //node.print(3);
653             if (i == restFormula.length()) {
654                 restFormula = null;
655             } else {
656                 restFormula = restFormula.substring(i);
657                 if (restFormula.charAt(0) == 0) {
658                     return null;
659                 }
660             }
661         }
662 
663         return restFormula;
664     }
665 
666     public void setValue(int type, String s) {
667         paused = false;
668         if (this.type == Cell.URL) {
669             updaterThread.run = false;
670             updaterThread = null;
671         }
672 
673         valueString = s;
674         this.type = type;
675         needRedisplay = true;
676         switch (type) {
677             case Cell.VALUE:
678                 setValue(Float.valueOf(s).floatValue());
679                 break;
680             case Cell.LABEL:
681                 printString = "l" + valueString;
682                 break;
683             case Cell.URL:
684                 printString = "u" + valueString;
685                 updaterThread = new CellUpdater(this);
686                 updaterThread.start();
687                 break;
688             case Cell.FORMULA:
689                 parseFormula(valueString, parseRoot = new Node());
690                 printString = "f" + valueString;
691                 break;
692         }
693         app.recalculate();
694     }
695 
696     public String getValueString() {
697         return valueString;
698     }
699 
700     public String getPrintString() {
701         return printString;
702     }
703 
704     public void select() {
705         selected = true;
706         paused = true;
707     }
708 
709     public void deselect() {
710         selected = false;
711         paused = false;
712         needRedisplay = true;
713         app.repaint();
714     }
715 
716     public void paint(Graphics g, int x, int y) {
717         if (selected) {
718             g.setColor(highlightColor);
719         } else {
720             g.setColor(bgColor);
721         }
722         g.fillRect(x, y, width - 1, height);
723         if (valueString != null) {
724             switch (type) {
725                 case Cell.VALUE:
726                 case Cell.LABEL:
727                     g.setColor(fgColor);
728                     break;
729                 case Cell.FORMULA:
730                     g.setColor(Color.red);
731                     break;
732                 case Cell.URL:
733                     g.setColor(Color.blue);
734                     break;
735             }
736             if (transientValue) {
737                 g.drawString("" + value, x, y + (height / 2) + 5);
738             } else {
739                 if (valueString.length() > 14) {
740                     g.drawString(valueString.substring(0, 14),
741                             x, y + (height / 2) + 5);
742                 } else {
743                     g.drawString(valueString, x, y + (height / 2) + 5);
744                 }
745             }
746         }
747         needRedisplay = false;
748     }
749 }
750 
751 
752 class Node {
753 
754     public static final int OP = 0;
755     public static final int VALUE = 1;
756     public static final int CELL = 2;
757     int type;
758     Node left;
759     Node right;
760     int row;
761     int column;
762     float value;
763     char op;
764 
765     public Node() {
766         left = null;
767         right = null;
768         value = 0;
769         row = -1;
770         column = -1;
771         op = 0;
772         type = Node.VALUE;
773     }
774 
775     public Node(Node n) {
776         left = n.left;
777         right = n.right;
778         value = n.value;
779         row = n.row;
780         column = n.column;
781         op = n.op;
782         type = n.type;
783     }
784 
785     public void indent(int ind) {
786         for (int i = 0; i < ind; i++) {
787             System.out.print(" ");
788         }
789     }
790 
791     public void print(int indentLevel) {
792         char l[] = new char[1];
793         indent(indentLevel);
794         System.out.println("NODE type=" + type);
795         indent(indentLevel);
796         switch (type) {
797             case Node.VALUE:
798                 System.out.println(" value=" + value);
799                 break;
800             case Node.CELL:
801                 l[0] = (char) ((int) 'A' + column);
802                 System.out.println(" cell=" + new String(l) + (row + 1));
803                 break;
804             case Node.OP:
805                 System.out.println(" op=" + op);
806                 left.print(indentLevel + 3);
807                 right.print(indentLevel + 3);
808                 break;
809         }
810     }
811 }
812 
813 
814 class InputField {
815 
816     int maxchars = 50;
817     int cursorPos = 0;
818     Applet app;
819     String sval;
820     char buffer[];
821     int nChars;
822     int width;
823     int height;
824     Color bgColor;
825     Color fgColor;
826 
827     public InputField(String initValue, Applet app, int width, int height,
828             Color bgColor, Color fgColor) {
829         this.width = width;
830         this.height = height;
831         this.bgColor = bgColor;
832         this.fgColor = fgColor;
833         this.app = app;
834         buffer = new char[maxchars];
835         nChars = 0;
836         if (initValue != null) {
837             initValue.getChars(0, initValue.length(), this.buffer, 0);
838             nChars = initValue.length();
839         }
840         sval = initValue;
841     }
842 
843     public void setText(String val) {
844         int i;
845 
846         for (i = 0; i < maxchars; i++) {
847             buffer[i] = 0;
848         }
849         if (val == null) {
850             sval = "";
851         } else {
852             sval = val;
853         }
854         nChars = sval.length();
855         sval.getChars(0, sval.length(), buffer, 0);
856     }
857 
858     public String getValue() {
859         return sval;
860     }
861 
862     public void paint(Graphics g, int x, int y) {
863         g.setColor(bgColor);
864         g.fillRect(x, y, width, height);
865         if (sval != null) {
866             g.setColor(fgColor);
867             g.drawString(sval, x, y + (height / 2) + 3);
868         }
869     }
870 
871     public void processKey(KeyEvent e) {
872         char ch = e.getKeyChar();
873         switch (ch) {
874             case '\b': // delete
875                 if (nChars > 0) {
876                     nChars--;
877                     sval = new String(buffer, 0, nChars);
878                 }
879                 break;
880             case '\n': // return
881                 selected();
882                 break;
883             default:
884                 if (nChars < maxchars && ch >= '0') {
885                     buffer[nChars++] = ch;
886                     sval = new String(buffer, 0, nChars);
887                 }
888         }
889         app.repaint();
890     }
891 
892     public void keyReleased(KeyEvent e) {
893     }
894 
895     public void selected() {
896     }
897 }
898 
899 
900 class SpreadSheetInput
901         extends InputField {
902 
903     public SpreadSheetInput(String initValue,
904             SpreadSheet app,
905             int width,
906             int height,
907             Color bgColor,
908             Color fgColor) {
909         super(initValue, app, width, height, bgColor, fgColor);
910     }
911 
912     @Override
913     public void selected() {
914         float f;
915         sval = ("".equals(sval)) ? "v" : sval;
916         switch (sval.charAt(0)) {
917             case 'v':
918                 String s = sval.substring(1);
919                 try {
920                     int i;
921                     for (i = 0; i < s.length(); i++) {
922                         char c = s.charAt(i);
923                         if (c < '0' || c > '9') {
924                             break;
925                         }
926                     }
927                     s = s.substring(0, i);
928                     f = Float.valueOf(s).floatValue();
929                     ((SpreadSheet) app).setCurrentValue(f);
930                 } catch (NumberFormatException e) {
931                     System.out.println("Not a float: '" + s + "'");
932                 }
933                 break;
934             case 'l':
935                 ((SpreadSheet) app).setCurrentValue(Cell.LABEL,
936                         sval.substring(1));
937                 break;
938             case 'u':
939                 ((SpreadSheet) app).setCurrentValue(Cell.URL, sval.substring(1));
940                 break;
941             case 'f':
942                 ((SpreadSheet) app).setCurrentValue(Cell.FORMULA,
943                         sval.substring(1));
944                 break;
945         }
946     }
947 }